/*
 * Copyright 2010-2012 Marcin Blazejewski
 *
 * This file is part of Ground0
 *
 * Ground0 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ground0 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ground0. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "properties.hpp"
#include "eye.hpp"
#include "pilot_eye.hpp"
#include "stuff.hpp"
#include "world.hpp"
#include "garage.hpp"
#include "lab.hpp"
#include "wloader.hpp"

#include <iostream>
#include <string>
#include <stdlib.h>
#include <time.h>
#include <SDL/SDL.h>
#include <math.h>

using namespace std;


#define CAP_FPS
#define DESIRED_FPS                    30
//#define DISPLAY_FPS

class Ground0 *game = NULL;

class Ground0 {
private:
    Eye                   *eye;
    Scene                 *scene;
    Controller            *controller;

    Pilot                  pilot;
    World                 *world;
    Garage                 garage;
  
    Lab                    lab;

    bool                   be_alive;

    float                  fov, target_fov;
    float                  persp_trans;


protected:
    void on_keyup(SDL_KeyboardEvent *ev);
    void on_keydown(struct SDL_KeyboardEvent *ev);

public:
    Ground0(int w, int h, int _bpp, bool _fs);
    virtual ~Ground0();

    void                   init(Properties &config);
    void                   run();
    void                   tick(unsigned int ms);
};

Ground0::Ground0(int w, int h, int bpp, bool fs) {
    eye = new Eye(w, h, bpp, fs, "Ground0");
    eye->load_font("data/font.bmp");

    target_fov = fov = 6.0;
    persp_trans = 0;
}

Ground0::~Ground0() {
    if (eye) delete eye;
    if (world) delete world;
}

void Ground0::init(Properties &config) {
    PropertyCursor mc(config, "model.");
    char namebuf[256];
    int i = 0;
    while (mc.has_next()) {
        memset(namebuf, 0, sizeof(namebuf));
        sprintf(namebuf, "s%d", i++);
        load_model(mc.get_next().c_str(), namebuf);
        
        garage.add_ship_model(namebuf);
    }

    garage.add_ship_model("test-hammer");
    garage.add_ship_model("test-ball");

    world = new World();
    world->set_ship_model(garage.get_current_model());
    pilot.set_target(world->get_ship());

    lab.init();

    //scene = world;
    //controller = &pilot;
    scene = &lab;
    controller = &lab;
    //scene = &garage;
    //controller = &garage;
}

void Ground0::on_keyup(SDL_KeyboardEvent *ev) {
    bool ctrl = ev->keysym.mod & KMOD_CTRL;

    if (!ctrl) {
        switch (ev->keysym.sym) {
        case SDLK_q:
            be_alive = false; 
            return;
        case SDLK_f:
            eye->toggle_fullscreen(); 
            return;
        case SDLK_F5: 
            //scene = world;
            //controller = &pilot;
            return;
        case SDLK_F6: 
            scene = &lab;
            controller = &lab;
            return;
        case SDLK_F7:
            scene = &garage;
            controller = &garage;
            return;
            /*
        case SDLK_o: 
            fog=!fog; 
            if (fog) {
                clip_dist = 480;
                glEnable(GL_FOG);
                glClearColor(fogColor[0], fogColor[1], fogColor[2], fogColor[3]);
            } else {
                clip_dist = 800;
                glDisable(GL_FOG); 
                glClearColor(0,0,0,0);
            }
            break;
        case SDLK_w:
            wireframe = !wireframe;
            if (wireframe) {
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                glDisable(GL_LIGHTING);
            } else {
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                glEnable(GL_LIGHTING);
            }
            break;
        case SDLK_c:
            cr_show = !cr_show; 
            break;
            */
            
        case SDLK_F1:    world->set_camera_mode(0); break;
        case SDLK_F2:    world->set_camera_mode(1); break;
        case SDLK_F3:    world->set_camera_mode(2); break;
        case SDLK_F4:    world->set_camera_mode(3); break;
            
        }
    }

    controller->on_keyup(ev);
}

void Ground0::on_keydown(SDL_KeyboardEvent *ev) {
    controller->on_keydown(ev);
}


void Ground0::run() {
    SDL_Event ev;
    Uint32 ticks1, ticks2, dt;
    Uint32 t1, t2, t3;
    Uint32 delay = 1000 / DESIRED_FPS;
    Uint32 frames = 0;
    Uint32 perf_t0 = SDL_GetTicks(), perf_t1 = 0;
    float perf_fps = 0;
    char txt[256];

    SDL_EventState(SDL_MOUSEMOTION, SDL_DISABLE);

    memset(txt, 0, sizeof(txt));
    be_alive = true;
    t2 = SDL_GetTicks();
    while (be_alive) {

        // reset view
        glMatrixMode(GL_MODELVIEW);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();

        scene->render();

        glEnable(GL_BLEND);
        glEnable(GL_TEXTURE_2D);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_LIGHTING);
        
        glColor4f(1.0, 1.0, 1.0, 1.0);

        sprintf(txt, "speed: %d", 123);
        eye->print(0, 0, txt);

        t1 = t2;
        t2 = SDL_GetTicks();

#ifdef DISPLAY_FPS
        if (frames > 15) {
            perf_t1 = SDL_GetTicks();
            sprintf(txt, "FPS: %d", (int) frames * 1000  / (perf_t1-perf_t0)  );
            perf_t0 = perf_t1;
            frames = 0;
        }
        
        eye->print(0, 50, txt);
#endif

        SDL_GL_SwapBuffers();
        frames++;
        
#ifndef CAP_FPS
        tick(t2-t1);
        scene->tick(t2-t1);
#endif

        if (SDL_PollEvent(&ev)) {
            switch (ev.type) {
            case SDL_QUIT: be_alive = 0; break;
            case SDL_VIDEORESIZE: eye->resize(ev.resize.w, ev.resize.h); break;
            case SDL_KEYUP: on_keyup(&ev.key); break;
            case SDL_KEYDOWN: on_keydown(&ev.key); break;
            case SDL_MOUSEBUTTONDOWN: controller->on_mouse_down(&ev.button); break;
            case SDL_MOUSEBUTTONUP: controller->on_mouse_up(&ev.button); break;
            }
        }

#ifdef CAP_FPS                
        ticks2=SDL_GetTicks();
        dt = ticks2 - ticks1;
        if (dt < delay) SDL_Delay( delay - dt );
        ticks1 = SDL_GetTicks();   
        
        tick(delay);
        scene->tick(delay);
#endif

    }

    perf_t1 = SDL_GetTicks();
    if (perf_t1 > perf_t0) perf_fps = frames * 1000.0f / (perf_t1 - perf_t0);
}


void Ground0::tick(unsigned int ms) {
    if (persp_trans > 0) {
        persp_trans -= ms * (1.0/2000);
        if (persp_trans < 0) persp_trans = 0;

        fov = 6.0 + (target_fov - 6.0) * (persp_trans);
        eye->set_fov(fov);

        lab.cam_r = 12.0 + (96.0-12) * (1.0-persp_trans);
        //lab.update_cam_pos();
    }
    else if (persp_trans < 0) {
        persp_trans += ms * (1.0/2000);
        if (persp_trans > 0) persp_trans = 0;

        fov = 6.0 + (target_fov - 6.0) * (1.0 + persp_trans);
        eye->set_fov(fov);

        lab.cam_r = 12.0 + (96.0-12) * (-persp_trans);
        //lab.update_cam_pos();
    }

}

/********************************************************************************
  MAIN
 *******************************************************************************/


extern "C" int main(int argc, char **argv) {
    int rc = 0;
    bool fs = false;
    int w = 0;
    int h = 0;
    int seed = time(0);
    Properties config;

    // parse cmd line
    if (argc > 1) {
        if ((strcmp(argv[1], "-f") == 0) || 
            (strcmp(argv[1], "--fullscreen") == 0)) {
            fs = true;
        }
    }

    // set default ship model
    // this can be overwritten by config file value
    config.set("model.1", "data/s1.obj");

    // try to load config file
    try {
        config.read_file("ground0.config");
    } catch (string &e) {
        cout << "[debug ] assuming default configuration, " << e << endl;
    }

    // get configuration from config file or assume default values
    w = config.get_int("width", 1024);
    h = config.get_int("height", 600);
    string fsopt = config.get_str("fullscreen", "false");
    fsopt = lower( fsopt );
    if ((fsopt.compare("true") == 0) || (fsopt.compare("t") == 0) ||
        (fsopt.compare("yes") == 0) || (fsopt.compare("y") == 0) ||
        (fsopt.compare("1") == 0) || (fsopt.compare("on") == 0)) {
            fs = true;
    }    

    try {
        cout << "[info  ] Initialize" << endl;
        //load_rules(config);

        srand(seed);
        cout << "[debug ] rand seed " << seed << endl;

        game = new Ground0(w, h, 32, fs);

        load_model("data/ast-1.obj", "a1");
        load_model("data/ast-2.obj", "a2");
        load_model("data/ast-3.obj", "a3");
        load_model("data/ast-4.obj", "a4");

        load_model("data/s1-c.obj", "s1-c");
        load_model("data/ast-1-c.obj", "ast-c");
        load_model("data/gate.obj", "gate");
        load_model("data/gate.obj", "gate-c");

        load_model("data/test-ball.obj", "test-ball");
        load_model("data/test-ball-c.obj", "test-ball-c");
        load_model("data/test-hammer.obj", "test-hammer");
        cout << "[info  ] prepare world" << endl;
        
        game->init(config);
        
        cout << "[info  ] Enter main loop" << endl;
        game->run();
        cout << "[info  ] Main loop finished" << endl;

        delete game;
    }
    catch (const string m) {
        cerr << "[FUBAR ] " << m << endl;
        rc = 99;
    }
    catch (const char *m) {
        cerr << "[FUBAR ] " << m << endl;
        rc = 99;
    }
    catch (...) {
        cerr << "[FUBAR ] no idea what went wrong, but it was bad..." << endl;
        rc = 99;
    }

    wloader_destroy();

    return rc;
}

/* EOF */


